Classification of Driver Distraction

This project aims to develop a machine learning system that can detect and classify different distracted states of car drivers. The main approach is to apply deep convolutional neural networks (CNNs). We will explore and experiment various CNN architectures, leveraged pre-trained networks (learning transfer), psuedo labelling, and potentially an emsenbles of several models to find the best classification. Results of this project may be used to further research and applied to as a part of an on-car online monitoring system where computer will decide to take-over control of the car if the driver is distracted and poses a potential accident.


In [8]:
from tensorflow.python.client import device_lib

[x.physical_device_desc for x in device_lib.list_local_devices() if x.device_type == 'GPU']
print(device_lib.list_local_devices())


[name: "/device:CPU:0"
device_type: "CPU"
memory_limit: 268435456
locality {
}
incarnation: 12325561800027376336
, name: "/device:GPU:0"
device_type: "GPU"
memory_limit: 78616985
locality {
  bus_id: 1
}
incarnation: 12013119733775262870
physical_device_desc: "device: 0, name: GeForce GTX 1050, pci bus id: 0000:01:00.0, compute capability: 6.1"
]

In [9]:
import math, os, sys
import numpy as np
from numpy.random import random, permutation, randn, normal
from matplotlib import pyplot as plt
%matplotlib inline

import keras
from keras import backend as k
from keras.utils.data_utils import get_file
from keras.models import Sequential, Model
from keras.layers.core import Flatten, Dense, Dropout, Lambda
from keras.layers import Input,  GlobalAveragePooling2D
from keras.layers.convolutional import Conv2D, MaxPooling2D, ZeroPadding2D
from keras.optimizers import SGD, RMSprop, Adam
from keras.preprocessing import image
from keras.layers.normalization import BatchNormalization
from keras.utils.np_utils import to_categorical
from keras.metrics import categorical_crossentropy
from keras.regularizers import l2,l1

import PIL
from PIL import Image
import bcolz
import pickle
from shutil import copyfile
from shutil import move
from glob import glob


Using TensorFlow backend.

In [10]:
%pwd


Out[10]:
'C:\\Users\\User\\Downloads\\udacity\\project'

creating validation set from traing set


In [11]:
current_dir = os.getcwd()
PROJECT_DIR = current_dir
path = current_dir+'/imgs/'
test_path = path + 'test/' #We use all the test data
train_path = path + '/train/'
result_path = path + '/results/'
valid_path = path + '/valid/'

In [12]:
'''
%cd $path
%mkdir valid
%mkdir results
%mkdir models'''


Out[12]:
'\n%cd $path\n%mkdir valid\n%mkdir results\n%mkdir models'

In [13]:
"""# Creating validation set
%cd $valid_path
%mkdir c0
%mkdir c1
%mkdir c2
%mkdir c3
%mkdir c4
%mkdir c5
%mkdir c6
%mkdir c7
%mkdir c8
%mkdir c9

%cd $path"""


Out[13]:
'# Creating validation set\n%cd $valid_path\n%mkdir c0\n%mkdir c1\n%mkdir c2\n%mkdir c3\n%mkdir c4\n%mkdir c5\n%mkdir c6\n%mkdir c7\n%mkdir c8\n%mkdir c9\n\n%cd $path'

In [14]:
os.listdir(train_path)


Out[14]:
['c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9']

In [15]:
class_modes = ['c0','c1','c2','c3','c4','c5','c6','c7','c8','c9']

for i in class_modes:
    print ('label {0} has {1:5d} images'.format(i,len([name for name in os.listdir(train_path+i) if os.path.isfile(os.path.join(train_path+i, name))])))


label c0 has  1989 images
label c1 has  1767 images
label c2 has  1817 images
label c3 has  1846 images
label c4 has  1826 images
label c5 has  1812 images
label c6 has  1825 images
label c7 has  1502 images
label c8 has  1152 images
label c9 has  1238 images

In [16]:
summ = float(0)
for i in class_modes:
    summ=summ+len([name for name in os.listdir(train_path+i) if os.path.isfile(os.path.join(train_path+i, name))])
summ


Out[16]:
16774.0

There are around 2000 images for each categories. It is probably a good idea to move 20% of images (400 images for each categories) to validation sets.


In [17]:
%cd $train_path


C:\Users\User\Downloads\udacity\project\imgs\train

Only run these Once


In [18]:
'''for label in class_modes:
    g = glob(label+"/*.jpg")
    shuffle = np.random.permutation(g)
    for i in range(500):
        move(shuffle[i], valid_path+shuffle[i])'''


Out[18]:
'for label in class_modes:\n    g = glob(label+"/*.jpg")\n    shuffle = np.random.permutation(g)\n    for i in range(500):\n        move(shuffle[i], valid_path+shuffle[i])'

Visulizing the data


In [19]:
def get_batches(dirname, 
                gen=image.ImageDataGenerator(), 
                shuffle=True,
                batch_size=1, 
                target_size=(224, 224), 
                class_mode = "categorical"):
    
    return gen.flow_from_directory(path+dirname, 
                                   target_size, 
                                   class_mode=class_mode, 
                                   shuffle=shuffle, 
                                   batch_size=batch_size)

def plots(ims, figsize=(12,6), rows=1, titles=True, interp=False):

    if type(ims[0]) is np.ndarray:
        ims = np.array(ims).astype(np.uint8)
        if (ims.shape[-1] != 3):
            ims = ims.transpose((0,2,3,1))
    f = plt.figure(figsize=figsize)
    for i in range(len(ims)):
        sp = f.add_subplot(rows, len(ims)//rows, i+1)
        sp.axis('Off')
        if titles is not None:
            sp.set_title(titles[i], fontsize=16)
        plt.imshow(ims[i], interpolation=None if interp else 'none')

batches = get_batches("valid", batch_size=6)
imgs , labels = next(batches)
#random images from validation 
plots(imgs, titles=labels, figsize=(20,15), rows =2)


Found 5000 images belonging to 10 classes.

In [20]:
batches = get_batches("train", batch_size=6)
imgs , labels = next(batches)
#random images from training 
plots(imgs, titles=labels, figsize=(20,15), rows =2)


Found 16774 images belonging to 10 classes.

Get the training and validation data

Only run these Once


In [ ]:


In [ ]:


In [21]:
"""def get_data(path, target_size = (224,224)):
    batches = get_batches(path, shuffle=False, batch_size=20, class_mode=None, target_size=target_size)
    return np.concatenate([batches.next() for i in range (len(batches.classes))])"""

def get_data(path, target_size = (224,224)):
    batches = get_batches(path, shuffle=False, batch_size=20, class_mode=None, target_size=target_size)
    return np.concatenate([batches.next() for i in range (len(batches.classes))])




#p1 = Process(target=get_data, args=("train",))
#p2 = Process(target=get_data, args=("valid",))
#p1.start()
#p1.join()
#p2.start()
#p2.join()



train_data = get_data("train")
#del train_data
valid_data = get_date("valid")
#del valid_data
#train_data.flush()
#train_data.close()
#valid_data.flush()
#valid_data.close()


Found 16774 images belonging to 10 classes.
---------------------------------------------------------------------------
MemoryError                               Traceback (most recent call last)
<ipython-input-21-e54e5c2cacb1> in <module>()
     19 
     20 
---> 21 train_data = get_data("train")
     22 #del train_data
     23 valid_data = get_date("valid")

<ipython-input-21-e54e5c2cacb1> in get_data(path, target_size)
      5 def get_data(path, target_size = (224,224)):
      6     batches = get_batches(path, shuffle=False, batch_size=20, class_mode=None, target_size=target_size)
----> 7     return np.concatenate([batches.next() for i in range (len(batches.classes))])
      8 
      9 

<ipython-input-21-e54e5c2cacb1> in <listcomp>(.0)
      5 def get_data(path, target_size = (224,224)):
      6     batches = get_batches(path, shuffle=False, batch_size=20, class_mode=None, target_size=target_size)
----> 7     return np.concatenate([batches.next() for i in range (len(batches.classes))])
      8 
      9 

~\Anaconda3\envs\tensorflow\lib\site-packages\keras\preprocessing\image.py in next(self)
   1133         # The transformation of images is not under thread lock
   1134         # so it can be done in parallel
-> 1135         return self._get_batches_of_transformed_samples(index_array)

~\Anaconda3\envs\tensorflow\lib\site-packages\keras\preprocessing\image.py in _get_batches_of_transformed_samples(self, index_array)
   1087 
   1088     def _get_batches_of_transformed_samples(self, index_array):
-> 1089         batch_x = np.zeros((len(index_array),) + self.image_shape, dtype=K.floatx())
   1090         grayscale = self.color_mode == 'grayscale'
   1091         # build batch of image data

MemoryError: 

In [ ]:


In [ ]:
def save_array(fname, arr):
    c=bcolz.carray(arr, rootdir=fname, mode='w')
    c.flush()

save_array('results/train_data.dat', train_data)
save_array('results/train_data.dat', train_data)

In [ ]:
def load_array(fname):
    return bcolz.open(fname)[:]


(valid_classes, train_classes, valid_labels, train_labels, valid_filenames, train_filenames) = get_classes(path)
valid_data = load_array(path+'results/valid_data.dat')
train_data = load_array(path+'results/train_data.dat')

2. Experiements

2.1. Benchmark

In this section I will use a fully connected network with no hidden layer, i.e., linear model. This is to provide a benchmark for other experiments developments.

  • I used batchnormalization right at the input layer to avoid any domination input values that could skew the output.
  • I activated the output with a softmax layer for 10 classes.
  • I will use 224x224 input shape, as the results we will have 1.5+ million parametters and easily overfitted with a linear model, hence, l2 regularization is used to minimize impact of overfitting.

In [ ]:
Linear_model = Sequential([
        BatchNormalization(axis=-1, input_shape=(224,224,3)),
        Flatten(),
        Dense(10, activation='softmax')
    ])

Linear_model.compile(Adam(lr=0.000001), loss='categorical_crossentropy', metrics=['accuracy'])
Linear_model.summary()

In [ ]:


In [ ]:


In [ ]: